from collections import namedtuple
from inspect import getgeneratorstate

# {0: [0, 1, 2, 3], 1: [1, 2, 3, 4], 2: [2, 3, 4, 5], 3: [3, 4, 5, 6], 4: [4, 5, 6, 7]}
data = dict(enumerate(map(lambda x: range(x, x + 4), range(5))))
data2 = dict(enumerate(map(lambda x: range(-x, -x + 4), range(5))))
# {0: [0, 1, 2, 3], 1: [-1, 0, 1, 2], 2: [-2, -1, 0, 1], 3: [-3, -2, -1, 0], 4: [-4, -3, -2, -1]}


def client_consume():
    """
    >>> subg = client_consume()
    >>> subg
    """
    print("normal fun")
    results = {}
    delegates = []
    for key, values in data.items():
        print("build group for", key)
        group = grouper(results, key)
        delegates.append(group)
        print("    (id of group", group, ")")
        next(group)
        for v in values:
            print("send", v, "for", key)
            group.send(v)
        try:
            # group.send(None)  # 结算
            pass
        except StopIteration as e:
            print("!!!!!!subGen异常", e)  # e 为None ,已经被yield from 处理
            raise e
    print("results", results)
    print("================================")
    for key, values in data2.items():
        print("build group for", key)
        group = delegates[key]
        # group = grouper(results, key)
        # next(group)
        print("    (id of group", group, ")", getgeneratorstate(group))
        # print(getgeneratorstate(next(group))) # 相当于发送了None到subGen,被0除异常
        for v in values:
            print("send", v, "for", key)
            group.send(v)
        try:
            group.send(None)  # 结算
        except StopIteration as e:
            print("!!!!!!subGen异常", e)  # e 为None ,已经被yield from 处理
            print("results", results)
            raise e
    print(results)


def grouper(results, key):  # 委托生成器

    while True:
        # for i in range(2):  # 可能会耗尽
        sgen = averager()
        print("gen delegate for", key, sgen)
        results[key] = yield from sgen  # 此处暂停,期待调用者发送数据到子生成器 averager

    print("委托继续执行", key, results[key])  # 运行到此处,则会产生 StopIteration!! 坑 容易搞错!
    # 使用 While True 期待 信道中数据都发送到子生成器
    # NOTE 相当于:await
    #   直到子生成器耗尽
    #   或子生成器返回--返回时,解释器抛出StopIteration,其值附加到异常对象上,委派则会恢复执行)
    # NOTE 作为最靠近客户端的生成器(管道起点),为了`省掉`client处理StopIteration异常,NOTE 委托生成器不应耗尽
    #   所以 NOTE 委托生成器一般 用 `无尽委托`
    #   但是While True不用担心永久阻塞的问题:
    #   因为: yield from 来自于 子生成器耗尽或 return expr.
    #   此时: 委托继续执行到下一个暂停点(构造一个新的subGen), 并将自身还给调用者


Result = namedtuple("Result", ["count", "avg"])


def averager():
    total = 0
    count = 0
    while True:
        recieved = yield
        if recieved == None:
            print("break outgo Result <解释器此时会抛出StopIteration 异常.> next key")
            print("    (不过在返回调用者之前,委托生成器会继续运行,或至下一个暂停)")
            break
        count += 1
        total += recieved
    return Result(count, total / count)


client_consume()
if __name__ == "__main__" and False:
    import doctest

    print("yieldfrom 测试开始")
    (ff, tt) = doctest.testmod()
    print("测试结束:统计-失败{},总计{}".format(ff, tt))
    print("√" * tt if tt > 0 and not ff else "无测试" if not ff else "×" * ff)
